/*
 *   Copyright(C) <2009>
 *
 *   <Sandeep K Sinha> Complete rewrite.
 *   <Rishi B Agrawal> <Vineet Agarwal> <Rohit Vashist>
 *   <Rohit K Sharma>
 *
 *   <sandeepksinha@gmail.com> <rishi.b.agrawal@gmail.com>
 *   <checkout.vineet@gmail.com> <rohit.k.vashist@gmail.com>
 *   <imreckless@gmail.com> <sneha.hendre@gmail.com>
 *
 *   This program is free software: you can redistribute it and/or modify it
 *   under the terms of the GNU General Public License as published by the
 *   Free Software Foundation, either version 3 of the License, or(at your
 *   option) any later version->
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE->  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <getopt.h>
#include <unistd.h>
#include <stdlib.h>
#include <linux/ioctl.h>
#include <pwd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "../include/ohsm.h"
#include "ohsm_u.h"


int ohsm_fd;
/*
 * This dbentry should be used for non real time purposes only. 
 * The reason being that by the time we look at it, the data might
 * get stale.
 * Remember that a state == 0 means, the entry is invalid.
 */

int ohsm_get_db_ent(char *mountpoint, struct ohsm_db_ent *dbentry)
{
	FILE *fp;
	struct ohsm_db ohsm_super_db;
	struct ohsm_db_ent *tmp_dbentry;
	struct ohsm_db *tmp_super;
	int count, i;
	int ret = 0;

	/*
	 * This is our db file as of today, which might change later.
	 */
	fp = fopen("/etc/ohsm/db","r");
	if (!fp) {
		printf("Failed to open file /etc/ohsm/db\n");
		return -1;
	}	

	/*
	 * First read the super block of the db. And then read the 
	 * db entries.
	 */
	 
	fread((void *)&ohsm_super_db,sizeof(struct ohsm_db), 1, fp);
	
	/*
	 * Make sure the magic number on disk, in the ohsm super block
	 * matches.
	 * Non-matching means this not even initialised and we cannot use it.  
	 * We simply return an error.
	 */
	if (ohsm_super_db.magic != 0xbabe) {
		printf("Invalid Magic: %d\n", ohsm_super_db.magic);
		fclose(fp);
		return -1;
	}
	
	/*
	 * Get the number of instances first.
	 */

	count = ohsm_super_db.num_instances;
	printf("Number of instances: %d\n", count);
	
	/*
	 * Allocate memory for all the entries to read it in memory.
	 */
	tmp_super = (struct ohsm_db *)malloc(sizeof(struct ohsm_db) + (count * sizeof(struct ohsm_db_ent)));
	if (!tmp_super) {
		printf("Not enough memory\n");
		goto error_out;;
	}	

	/*
	 * Go to the begining of the db.
	 */
	fseek(fp, 0L, SEEK_SET);

	/*
	 * Read all the entries in memory.
	 */
	ret = fread((void *)tmp_super, (sizeof(struct ohsm_db) + (count * sizeof(struct ohsm_db_ent))), 1, fp);
	
	/*
	 * Pointer to the end of the super block and begining of the entries.
	 */
	tmp_dbentry = tmp_super->dbentry;

	/*
	 * Iterate to search for the specific mountpoint.
	 */
	for (i = 0; i < count; ++i) {
		/*
		 * If we get a match, copy the information.
		 */
		if (tmp_dbentry[i].state == 1 && !(strncmp(tmp_dbentry[i].mountpoint, mountpoint, strlen(mountpoint)))) {
  			memcpy((void *)dbentry, (void *)&tmp_dbentry[i], sizeof(struct ohsm_db_ent));
			goto clean_out;
		}
	}
	/*
	 * Cleanup.
	 */

error_out:
	free(tmp_super);
	fclose(fp);	
	return -1;

clean_out:
	free(tmp_super);
	fclose(fp);
	return 0;
}

/*
 * This routine searches the ohsm database and try to search for a specific mountpoint,
 * passed as the parameter.
 */

int ohsm_search_db(char *mountpoint)
{
	FILE *fp;
	struct ohsm_db ohsm_super_db;
	struct ohsm_db_ent *tmp_dbentry;
	struct ohsm_db *tmp_super;
	int count, i;
	int ret = 0;

	/*
	 * Open the ohsm database file.
	 */
	fp = fopen("/etc/ohsm/db","r");
	if (!fp) {
		printf("Failed to open file /etc/ohsm/db\n");
		return -1;
	}

	/*
	 * Read the ohsm db super block.
	 */

	fread((void *)&ohsm_super_db,sizeof(struct ohsm_db), 1, fp);
	/*
	 * Makee sure that the metadata is not corrupted.
	 */
	if (ohsm_super_db.magic != 0xbabe) {
		printf("Invalid Magic: %d\n", ohsm_super_db.magic);
		fclose(fp);
		return -1;
	}
	
	/*
	 * Extract the number of entries and read everything in memory.
	 */

	count = ohsm_super_db.num_instances;
	printf("Number of instances: %d\n", count);
	tmp_super = (struct ohsm_db *)malloc(sizeof(struct ohsm_db) + (count * sizeof(struct ohsm_db_ent)));
	if (!tmp_super) {
		printf("Not enough memory\n");
		return -1;
	}	
	
	/*
	 * Get at the begining of the database.
	 */
	fseek(fp, 0L, SEEK_SET);

	/*
	 * Read the metadata and the entries in memory.
	 */

	fread((void *)tmp_super, (sizeof(struct ohsm_db) + (count * sizeof(struct ohsm_db_ent))), 1, fp);
	
	/*
	 * Point to the begining of entries.
	 */
	tmp_dbentry = tmp_super->dbentry;

	/*
	 * Search for what you looking forward to.
	 */
	for (i = 0; i < count; ++i) {
		if (tmp_super->dbentry[i].state == 1 && !(strncmp(tmp_super->dbentry[i].mountpoint, mountpoint, strlen(mountpoint))))
			goto clean_out;

	}

	/*
	 * Cleanup.
	 */

	free(tmp_super);
	fclose(fp);	
	return -1;

clean_out:
	free(tmp_super);
	fclose(fp);
	return 0;
}

/*
 * This routine is used to search for an entry and if its not there add to it.
 * If the entry is already there we return with an error, that already enabled.
 */


int ohsm_add_update_db(struct ohsm_db_ent *dbentry)
{
	FILE *fp;
	struct ohsm_db ohsm_super_db;
	struct ohsm_db_ent *tmp_dbentry;
	struct ohsm_db *tmp_super;
	int count, i, free_idx = -1;
	int ret = 0;

	/*
	 * Open the ohsm database file.
	 */
	fp = fopen("/etc/ohsm/db","r+");
	if (!fp) {
		printf("Failed to open file /etc/ohsm/db\n");
		return -1;
	}	

	/*
	 * Read the ohsm metadata in memory.
	 */

	fread((void *)&ohsm_super_db,sizeof(struct ohsm_db), 1, fp);

	/*
	 * Check if the headers have valid magic number.
	 */

	if (ohsm_super_db.magic != 0xbabe) {
		printf("OHSM Magic does not match\n");
	
		/*
		 * We need to dump new metadata here.
		 * Initialise the relevant data structures and dump it.
		 */
		ohsm_super_db.magic = 0xbabe;
		ohsm_super_db.num_instances = 0;

		/*
		 * Lets go the begining of the db.
		 */
		fseek(fp, 0L, SEEK_SET);

		/*
		 * Dump the new metadata here.
		 */
		 
		fwrite((void *)&ohsm_super_db, sizeof(struct ohsm_db), 1, fp);
	}
	
	count = ohsm_super_db.num_instances;
	printf("Number of instances found = %d\n", count);

	/*
	 * Allocating space for one extra so that if we make a new entry.
	 */
	fseek(fp, 0L, SEEK_SET);

	/*
	 * Lets read the entires now. We allocate memory for a extra entry 
	 * anticipating that we might have to write a new entry there.
	 */
	tmp_super = (struct ohsm_db *)malloc(sizeof(struct ohsm_db) + (count + 1) * sizeof(struct ohsm_db_ent)); 
	fread((void*)tmp_super, sizeof(struct ohsm_db) + (count * sizeof(struct ohsm_db_ent)), 1, fp);
	
	/*
	 * Get to the begining of the entries.
	 */

	tmp_dbentry = tmp_super->dbentry;

	/*
	 * Lets search for what we are looking for.
	 */
	for (i = 0; i < count; ++i) {
		/*
		 * Why not see if we get some free entry.
		 * We might use the same information to add the entry,
		 * incase we don't get the entry existing.
		 */

		if (tmp_dbentry[i].state == 0 && free_idx == -1) {
			free_idx = i;
			continue;
		} else if (tmp_dbentry[i].state == 1 && !strcmp(tmp_dbentry->mountpoint, dbentry->mountpoint)) {
			
			/*
			 * We got the entry, lets update it.
			 */
			memcpy((void *)&tmp_dbentry[i], (void*) dbentry, sizeof(struct ohsm_db_ent)); 
			fseek(fp, 0L, SEEK_SET);
		
			/*
			 * If a entry is being invalidated and its the last one,
			 * We can remove it from the num of instances.
			 */
			if (dbentry->state == 0 && (i+1 == count)) {
				tmp_super->num_instances--;
			}	
			
			/*
			 * We would require to flush the information back to db on disk.
			 */
			fwrite((void*)tmp_super, sizeof(struct ohsm_db) + (count * sizeof(struct ohsm_db_ent)), 1, fp);
			goto clean_out;
		}
	}	
		/*
		 * If no free entry found, write at the end of the file.
		 * Increment the number of instances and set the free 
		 * index to the end.
		 */
		printf("Value of free_idx = %d\n",free_idx);
		if (free_idx == -1) {
			free_idx = count;
			tmp_super->num_instances++;
		}

		/*
		 * Print some debug messages.
		 */
		printf("Value of free_idx = %d\n",free_idx);
		printf("Number of instances = %d\n",tmp_super->num_instances);

		/*
		 * Copy the information into incore db, change its state.
		 */
  		dbentry->state = 1;
		memcpy((void *)&tmp_dbentry[free_idx], (void*) dbentry, sizeof(struct ohsm_db_ent)); 
		
		/*
		 * Make sure we are at the begining of the db.
		 * Dump the in core db to the db file on disk.
		 */
		fseek(fp, 0L, SEEK_SET);
		ret = fwrite((void *)tmp_super, sizeof(struct ohsm_db) + ((count + 1) * sizeof(struct ohsm_db_ent)), 1, fp);
		printf("Value of ret = %d\n",ret);

clean_out:
	fclose(fp);
	return 0;

}

/*
 * This routine is called at the time of enabling ohsm or for ohsm --fsck.
 * This makes sure that all the files in the file sytsem abide by the 
 * allocation policy on the system.
 * This is a recursive function.
 * Allocpol and dir_home_tid are passed as they are used by ohsm_get_tier.
 */
   
int ohsm_walk_dir_alloc_sync(char *name, struct ohsm_allocation_policy *allocpol, unsigned char dir_home_tid)
{

	int tmp, ret;  /* number of characters read from a directory entry */
	struct direct *dentry;  /* pointer to a directory entry */
  	struct ohsm_debug_reloc reloc_demo;
  	struct ohsm_inode_info ohsm_inode;
	DIR *DirPtr;  /* pointer to the directory */
	struct stat st;
	char path[MAX_CHARS];
	unsigned char tier;

	/*
	 * Open the passed directory.
	 */
	DirPtr = opendir(name);
	while (1)  {
		dentry = readdir(DirPtr);
			if (dentry == 0) break;  /* reached end of directory entries */
				/* process file (other than . and .. and the ones starting with .) */
			if (strcmp(dentry->d_name,".") != 0 &&
				strcmp(dentry->d_name,"..") != 0 && 
				dentry->d_name[0] != '.') {
			   	/*
				 * Create the path.
				 */
				path[0] = '\x0';
			   	strcat(path,name);
			   	strcat(path,"/");
			   	strcat(path, dentry->d_name);
			   	tmp = lstat(path, &st); 

				/*
				 * Fill the inode info used by ohsm.
				 */
				ohsm_inode.i_uid = st.st_uid;
				ohsm_inode.i_gid = st.st_gid;
				ohsm_inode.i_ino = st.st_ino;
				ohsm_inode.i_size = st.st_size;
				ohsm_inode.i_mode = st.st_mode;

				/*
				 * This ioctl just gets the home_tier_id for the 
				 * specified inode.
				 */
				ret = ioctl(ohsm_fd, IOC_OHSM_GET_INO_INFO, &ohsm_inode);

				/*
				 * We need to have the ohsm_home_tid to call ohsm_get_tier.
				 * So, check if the ioctl passed.
				 */
				if (!ret) {
					tier = ohsm_get_tier(&ohsm_inode, path, allocpol, dir_home_tid);
					
					/*
					 * See if the home_tier != dest_tier. If so, relocate.
					 */
					if (tier != ohsm_inode.ohsm_home_tid) {

						/*
						 * Prepare for relocation.
						 */
						reloc_demo.inum = st.st_ino;
						reloc_demo.dest = tier;

						/*
						 * This ioctl relocates the specific inode to the 
						 * respective inode.
						 */
						ret = ioctl(ohsm_fd, IOC_OHSM_DEMO_RELOC, &reloc_demo);
						if (ret)
							printf("Relocation failed for %s\n",path);
					}   
				} else {
					printf("Something screwed up while getting inode info for %s\n", path);
				}

				/*
				 * Make a recursive call for directories.
				 */
			
				if (S_ISDIR(st.st_mode))
					 	ohsm_walk_dir_alloc_sync(path, allocpol, ohsm_inode.ohsm_home_tid); 
		 }
	} /* End of while */
}

/*
 * print_ohsm_error() displays OHSM_ERRor messages
 * corresponding to OHSM_ERRor number in parameter ch
 */

void
print_ohsm_error(int ch)
{

  if (ch)
	fprintf(stderr, "\nOHSM: error from the OHSM Kernel Driver.");

	switch (ch) {

	case OHSM_ERR_DISP_DEVICE:
      fprintf(stderr, "\nOHSM: error displaying device info");
      break;

	case OHSM_ERR_DISP_ALLOC:
      fprintf(stderr, "\nOHSM: error displaying allocation policy");
      break;

	case OHSM_ERR_DISP_RELOC:
      fprintf(stderr, "\nOHSM: error displaying relocation policy");
      break;

	case OHSM_ERR_MNTPNT:
      fprintf(stderr, "\nOHSM: Incorrect Mountpoint.");
      break;

	case OHSM_ERR_ADMIN:
      fprintf(stderr, "\nOHSM: Incorrect admin pointer.");
      break;

	case OHSM_ERR_ALLOC_PTR:
      fprintf(stderr, "\nOHSM: error in allocation policy.");
      break;

	case OHSM_ERR_ALLOC:
      fprintf(stderr, "\nOHSM: Incorrect allocation policy.");
      break;

	case OHSM_ERR_RELOC_PTR:
      fprintf(stderr, "\nOHSM: error in relocation policy.");
      break;

	case OHSM_ERR_RELOC:
      fprintf(stderr, "\nOHSM: Incorrect relocation policy.");
      break;

	case OHSM_ERR_RELOCATE:
      fprintf(stderr, "\nOHSM: error relocating files.");
      break;

	case OHSM_ERR_RULE_BITMAP:
      fprintf(stderr, "\nOHSM: Rule bitmap incorrect.");
      break;

	case OHSM_ERR_ALREADY_DISABLED:
      fprintf(stderr, "\nOHSM: OHSM already disabled.");
      break;

	case OHSM_ERR_OHSM_INACTIVE:
      fprintf(stderr, "\nOHSM: OHSM is disabled now.");
      break;

	case OHSM_ERR_FS:
      fprintf(stderr, "\nOHSM: error returned from the file system.");
      break;

	case OHSM_ERR_NULL_PTR:
      fprintf(stderr, "\nOHSM: Null Pointer.");
      break;

	case OHSM_ERR_ENOMEM:
      fprintf(stderr, "\nOHSM: Memory allocation failed.");
      break;

	case OHSM_ERR_RELOC_ON:
      fprintf(stderr, "\nOHSM: Relocation in progress.");
      break;

	case OHSM_ERR_RELOC_INACTIVE:
      fprintf(stderr, "\nOHSM: OHSM is currently disabled.");
      break;

	case OHSM_ERR_ALREADY_EN:
      fprintf(stderr, "\nOHSM: OHSM is already active.");
      break;

	case OHSM_ERR_INO_NOEXIST:
      fprintf(stderr, "\nOHSM: File does not exist.");
      break;

	case OHSM_SUCCESS:
      fprintf(stderr, "\nOHSM: Command executed successfully.");
      break;

	default:
      perror("\nOHSM:");
      break;
    }
}

/*
 * print_tier_info() displays the tier device
 * information currently in use
 */

void
get_tier_info(char *mountpoint)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;

  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  if (ret) {
	  printf("OHSM is not enabled.\n");
	  return;
  }
  print_device_topology(&dbentry.devinfo);
}

/*
 * print_alloc_policy() displays the current
 * allocation policy in use
 */

void
get_alloc_policy(char *mountpoint)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;

  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  if (ret) {
	  printf("OHSM is not enabled.\n");
	  return;
  }
  print_allocation_policy(&dbentry.allocpol);
}

/*
 * print_reloc_policy() displays the current
 * relocation policy in use
 */

void
get_reloc_policy(char *mountpoint)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;

  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  if (ret) {
	  printf("OHSM is not enabled.\n");
	  return;
  }
  print_relocation_policy(&dbentry.relocpol);
}

/*
 * run_debug() displays debugging information
 * 1. Displays sam table
 * 2. Displays inode info
 * 3. Runs a demo for relocation on specified inode
 */

int
debug_relocate(void)
{

  int ret = 0;
  struct ohsm_debug_reloc reloc_demo;

  fprintf(stderr, "\nOHSM: Enter inode Number : ");
  scanf("%lu", &reloc_demo.inum);
  fprintf(stderr, "\nOHSM: Enter destination Tier : ");
  scanf("%d", &reloc_demo.dest);
  ret = ioctl(ohsm_fd, IOC_OHSM_DEMO_RELOC, &reloc_demo);
  if (ret)
	fprintf(stderr, "\nOHSM: Unable to run relocation demo.");
  else
	print_ohsm_error(ret);
  return ret;
}

int
debug_get_sam(void)
{

  int ret = 0;

  ret = ioctl(ohsm_fd, IOC_OHSM_DISPLAY_SAM, NULL);
  if (!ret)
	fprintf(stderr, "\nOHSM: SAM printed. Check dmesg.");
  else
	print_ohsm_error(ret);
  return ret;
}

int
debug_get_files(void)
{

  int ret = 0;

  ret = ioctl(ohsm_fd, IOC_OHSM_DISPLAY_INODES, NULL);
  if (!ret)
	fprintf(stderr, "\nOHSM: Use dmesg to see results.");
  else
	print_ohsm_error(ret);
  return ret;
}

/*
 * _ohsm_select_relocate() is used for triggering
 * relocation uses rule bitmap to identify
 * rules
 */

int
_ohsm_select_relocate(unsigned long rule_bitmap)
{

  int ret = 0;
  char input;

  fprintf(stderr, "\nOHSM: Are you sure(y/n) : ");
  input = getchar();

  if (input == 'y' || input == 'Y')
	ret = ioctl(ohsm_fd, IOC_OHSM_SELECT_RELOCATE, &rule_bitmap);
  else
	fprintf(stderr, "\nOHSM: Relocation Cancelled Successfully.");

  print_ohsm_error(ret);
  return ret;
}

int
ohsm_select_relocate(char *mountpoint, int argc, char *argv[])
{

  int i, ret = 0;
  unsigned long rule_bitmap = 0, temp = 1, temp2;

  for (i = 0; i < argc - 2; i++) {
	temp = 1;
	temp2 = strtoul(argv[i + 2], NULL, 10);
	if (temp2 > OHSM_MAX_POLICIES) {
		fprintf(stderr, "\nOHSM: Invalid rule number %lu. \
				Maximum number of rules supported are %d",
		temp2, OHSM_MAX_POLICIES);
		ret = OHSM_ERROR;
	}
      rule_bitmap = rule_bitmap | temp << (temp2 - 1);
  }

  if (rule_bitmap == 0) {
		fprintf(stderr, "\nOHSM: Invalid rule number specified");
		ret = OHSM_ERROR;
  }
  ret = _ohsm_select_relocate(rule_bitmap);

  return ret;
}

int
ohsm_relocate(char *mountpoint, unsigned long rule)
{

  int ret = 0, i;
  unsigned long temp = 1;
  unsigned long r_bitmap = 0;
  struct ohsm_db_ent dbentry;

  ret = ohsm_get_db_ent(mountpoint, &dbentry);

  if (ret) {
	  printf("OHSM is not enabled.\n");
  } else {
	if (rule == 0) {
	  for(i=1; i < dbentry.relocpol.cnt_policies; ++i)
		r_bitmap = r_bitmap | temp << (i - 1); 
  	} else 
		r_bitmap = r_bitmap | temp << (rule -1);
	printf("The rule bitmap is %lu\n", r_bitmap);
  	ret = ohsm_walk_dir_relocate(mountpoint, &dbentry.relocpol, r_bitmap); 
  }	  
  return ret;
}

/* disable_OHSM() disables OHSM
 * sets OHSM_enabled flag to 0
 */

int
ohsm_disable(char *mountpoint)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;
  
  /*
  * Search in the database for an entry.
  * If we dont have an entry with state as ENABLED,
  * means its already disabled.
  */
  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  
  /*
  * The call was successful. We have got an entry.
  * Send the ioctl to disable in kernel first and then
  * update the db.
  */
  if (!ret) {

	ret = ioctl(ohsm_fd, IOC_OHSM_DISABLE, NULL);
  	if (!ret) {
		fprintf(stderr, "OHSM: OHSM disabled successfully.\n");
		dbentry.state = 0;	 
    	ohsm_add_update_db(&dbentry);
		return ret;	
  	}
	print_ohsm_error(ret);
  }
  printf("OHSM already disabled.\n");  
  return ret;
}

/* OHSM_start() takes input the policy files and
 * calls the parser to parse the file.
 * After successfully parsing the file it passes policies
 * to OHSM char driver to enable OHSM
 */

int
ohsm_enable(char *alloc_file, char *reloc_file, char *tier_file,
	     char *mountpoint, char *device_file)
{

  int ret = 0;
  struct ohsm_struct_for_ioctl input_to_kernel;
  struct ohsm_db_ent db_entry;

  /*
   * See, if we already have an entry with state = ENABLED.
   */
  ret = ohsm_search_db(mountpoint);
  if (!ret) {
	  printf("OHSM already enabled\n");
	  return 0;
  }	 

  /*
   * Allocation of memory for various policies.
   */
  dev_info =
   (struct ohsm_tier_dev_info *) malloc(sizeof(struct ohsm_tier_dev_info));
  allocpol =
   (struct ohsm_allocation_policy *)
    malloc(sizeof(struct ohsm_allocation_policy));
  relocpol =
   (struct ohsm_reloc_policy *)malloc(sizeof(struct ohsm_reloc_policy));

  /*
   * Check if any of the allocation failed.
   */
  if (!dev_info || !allocpol || !relocpol) {
	fprintf(stderr, "\nOHSM: insufficeint memory");
	return OHSM_ERROR;
  }

  /*
  * Initilize the various data structures.
  */
  memset(dev_info, 0, sizeof(*dev_info));
  memset(allocpol, 0, sizeof(*allocpol));
  memset(relocpol, 0, sizeof(*relocpol));
  /*
   * Get the device topology from libdevmaper.
   */

  ret = ohsm_get_device_topology(device_file);
  if (ret) { 
	fprintf(stderr, "\nOHSM: Device topology cannot be fetched");
	return ret;
  }

  /*
   * Call the parser to fill in the tier device information.
   */

  ret = ohsm_get_tier_device_info(tier_file);
  if (ret) {
	fprintf(stderr, "\nOHSM: Parse error. Tier device file = %s", tier_file);
	return ret;
  }

  /*
   * Call the parser to fill in the allocation policies.
   */
  ret = ohsm_get_allocation_policy(alloc_file);
  if (ret) {
	fprintf(stderr, "\nOHSM: Parse error. Allocation policy file %s",
	      alloc_file);
	return OHSM_ERROR;
  }

  /*
   * Call the parser to fill in the relocation policies.
   */
  ret = ohsm_get_relocation_policy(reloc_file);
  if (ret) {
	fprintf(stderr, "\nOHSM: Parse error. Relocation policy file %s",
	      reloc_file);
	return OHSM_ERROR;
  }

  /*
   * Printing all the policy information.
   */
  print_allocation_policy(allocpol);
  print_device_topology(dev_info);
  print_relocation_policy(relocpol);

  /*
   * Prepare the data structure for ioctl for enabling OHSM.
   */
  memset(&input_to_kernel, 0, sizeof(input_to_kernel));
  input_to_kernel.ptr_reloc_policy = relocpol;
  input_to_kernel.ptr_alloc_policy = allocpol;
  input_to_kernel.ptr_dev_file = dev_info;
  strcpy(input_to_kernel.mountpoint, mountpoint);
  strcpy(input_to_kernel.device, device_file);

  memcpy((void *)&db_entry.relocpol, (void *)relocpol, sizeof(struct ohsm_reloc_policy));
  memcpy((void *)&db_entry.allocpol, (void *)allocpol, sizeof(struct ohsm_allocation_policy));
  memcpy((void *)&db_entry.devinfo, (void *)dev_info, sizeof(struct ohsm_tier_dev_info));
  strcpy(db_entry.mountpoint, mountpoint);
  strcpy(db_entry.device, device_file);
  db_entry.state = 1;

  /*
   * IOCTL to enable OHSM.
   */
  ret = ioctl(ohsm_fd, IOC_OHSM_ENABLE, &input_to_kernel);
  if (ret) {
	/*
	 * Free the allocated memeory.
	 */
	print_ohsm_error(ret);
	return ret;
  }

 /*
  * Lets update the userspace database about 
  * what we have done recently.
  */
  if (ohsm_add_update_db(&db_entry)) {
	printf("Something screw up !!!!!\n");
	return -1;
  }	
  
  ohsm_walk_dir_alloc_sync(mountpoint, &db_entry.allocpol, 0);	
  
  printf("\n");  
  fprintf(stderr, "\nOHSM: OHSM Enabled Successfully");
  return OHSM_SUCCESS;
}

/* OHSM_status() displays the status of OHSM
 * displays enabled or disabled corresponding
 * to the OHSM state.
 */

void
ohsm_status(char* mountpoint)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;

  ret = ohsm_search_db(mountpoint);
  if (!ret)
	fprintf(stderr, "\nOHSM is enabled");
  else
	fprintf(stderr, "\nOHSM is disabled");
}


int
set_tier_info(char *mountpoint, char *device, char *tier_file)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;

  /*
   * Allocation of memory
   */
  dev_info =
   (struct ohsm_tier_dev_info *) malloc(sizeof(struct ohsm_tier_dev_info));
  memset(dev_info, 0, sizeof(struct ohsm_tier_dev_info));

  /*
   * Check if the allocation failed.
   */

  if (!dev_info) {
	fprintf(stderr, "\nOHSM: insufficeint memory");
	return OHSM_ERROR;
  }

  /*
   * Initilize the various data structures.
   */
  dev_info->dinfo_count = 0;

  /*
   * Get the device topology from libdevmaper.
   */

  ret = ohsm_get_device_topology(device);

  if (ret) {
	fprintf(stderr, "\nOHSM: Device topology cannot be fetched");
	return ret;
  }

  /*
   * Call the parser to fill in the tier device information.
   */

  ret = ohsm_get_tier_device_info(tier_file);
  if (ret) {
	fprintf(stderr, "\nOHSM: Parse error. Tier device file = %s", tier_file);
	return ret;
  }

  ret = ioctl(ohsm_fd, IOC_OHSM_SET_TIER_INFO, dev_info);
  if (ret) {
	fprintf(stderr, "\nOHSM: Unable to assign tier device info");
	print_ohsm_error(ret);
	return ret;
  }

  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  if (ret) {
	printf("Something screwed up, OHSM enabled in kernel\n");
	printf("Without a entry in ohsm db\n");
  	return ret;	
  }	
  memcpy((void *)&dbentry.devinfo, (void *)dev_info, sizeof(struct ohsm_tier_dev_info));;
  ohsm_add_update_db(&dbentry);
  
  fprintf(stderr, "\nOHSM: New Tier Device Information Entered Successfully");
  return OHSM_SUCCESS;
}

int
set_reloc_policy(char *mountpoint, char *reloc_file)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;
  /*
   * Allocation of memory
   */
  relocpol =
   (struct ohsm_reloc_policy *) malloc(sizeof(struct ohsm_reloc_policy));
  /*
   * Check if the allocation failed.
   */

  if (!relocpol) {
	fprintf(stderr, "\nOHSM: insufficeint memory");
	return OHSM_ERROR;
  }
  /*
   * Initilize the various data structures.
   */
  relocpol->cnt_policies = 0;

  /*
   * Call the parser to fill in the relocation policies.
   */
  ret = ohsm_get_relocation_policy(reloc_file);
  if (ret) {
	fprintf(stderr, "\nOHSM: Parse error. Relocation policy file %s",
	      reloc_file);
    return OHSM_ERROR;
  }
  /*
   * IOCTL to Set Relocation Policy.
   */
  ret = ioctl(ohsm_fd, IOC_OHSM_SET_RELOC_POL, relocpol);
  if (ret) {
	/*
	* Print the error.
	*/
	print_ohsm_error(ret);
	return ret;
  }
  
  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  if (ret) {
	printf("Something screwed up, OHSM enabled in kernel\n");
	printf("Without a entry in ohsm db\n");
  	return ret;	
  }	
  memcpy((void *)&dbentry.relocpol, (void *)relocpol, sizeof(struct ohsm_reloc_policy));;
  ohsm_add_update_db(&dbentry);

  fprintf(stderr, "\nOHSM: New Relocation Policy Entered Successfully");
  return OHSM_SUCCESS;
}

int
set_alloc_policy(char *mountpoint, char *alloc_file)
{

  int ret = 0;
  struct ohsm_db_ent dbentry;
  /*
   * Allocation of memory
   */
  allocpol =
   (struct ohsm_allocation_policy *)
    malloc(sizeof(struct ohsm_allocation_policy));

  /*
   * Check if the allocation failed.
   */

  if (!allocpol) {
	fprintf(stderr, "\nOHSM: insufficeint memory");
	return OHSM_ERROR;
  }

  /*
   * Call the parser to fill in the allocation policies.
   */

  ret = ohsm_get_allocation_policy(alloc_file);
  if (ret) {
	fprintf(stderr, "\nOHSM: Parse error. Allocation policy file %s",
	      alloc_file);
	return OHSM_ERROR;
  }

  /*
   * IOCTL to Set Allocation Policy.
   */
  ret = ioctl(ohsm_fd, IOC_OHSM_SET_ALLOC_POL, allocpol);
  if (ret) {
	/*
	 * Print the error.
	 */
	print_ohsm_error(ret);
	return ret;
  }
  
  ret = ohsm_get_db_ent(mountpoint, &dbentry);
  if (ret) {
	printf("Something screwed up, OHSM enabled in kernel\n");
	printf("Without a entry in ohsm db\n");
  	return ret;	
  }	
  memcpy((void *)&dbentry.allocpol, (void *)allocpol, sizeof(struct ohsm_allocation_policy));;
  ohsm_add_update_db(&dbentry);
  fprintf(stderr, "\nOHSM: New Allocation Policy Enforced");
  return OHSM_SUCCESS;
}

void
ohsm_help()
{
  fprintf(stderr, "\n   Usage: ohsm -m <mountpoint>[optstring ] [parameters] \n");
  fprintf(stderr, "\n   -e    --enable \t\t enable ohsm");
  fprintf(stderr, "\n   -d    --disable \t\t disable ohsm");
  fprintf(stderr, "\n   -n    --get_state \t\t get status of ohsm");
  fprintf(stderr, "\n   -v    --debug_ioctl \t\t ioctl defined for debug");
  fprintf(stderr, "\n   -r    --relocate \t\t relocate files");
  fprintf(stderr, "\n   -i    --device_info \t\t device info");
  fprintf(stderr, "\n   -a    --alloc_policy \t alloc_policy");
  fprintf(stderr, "\n   -p    --reloc_policy \t relocation policy ");
  fprintf(stderr, "\n   -s    --set_value \t\t set values");
  fprintf(stderr, "\n   -g    --get_value \t\t get values");
  fprintf(stderr, "\n   -w    --device_name \t\t device name ");
  fprintf(stderr, "\n   -x    --tierfile \t\t tier file");
  fprintf(stderr, "\n   -y    --allocfile \t\t allocation file");
  fprintf(stderr, "\n   -z    --relocfile \t\t relocation file");
  fprintf(stderr, "\n   -m    --mntpt \t\t mountpoint");
  fprintf(stderr, "\n   -t    --sam \t\t\t Block group mapping table");
  fprintf(stderr, "\n   -f    --files \t\t file on the mounted file system");
  fprintf(stderr, "\n   -o    --options_rules\t to\
 provide the rule for relocation\n");
}

/*
 *The main() function handles all the
 * commands OHSM provides
 */

int
main(int argc, char *argv[])
{

  int opt = 0, cmd_dbg = 0;
  int ret = 0, cmd = 0;
  unsigned int optionbits = 0;
  unsigned long rule;

  struct option longopts[] = {
    {"help", 0, NULL, 'h'},
    {"enable", 0, NULL, 'e'},
    {"disable", 0, NULL, 'd'},
    {"get_state", 1, NULL, 'n'},
    {"debug_ioctl", 1, NULL, 'v'},
    {"relocate", 1, NULL, 'r'},
    {"device_info", 0, NULL, 'i'},
    {"alloc_policy", 0, NULL, 'a'},
    {"reloc_policy", 0, NULL, 'p'},
    {"set_value", 1, NULL, 's'},
    {"get_value", 1, NULL, 'g'},
    {"device_name", 1, NULL, 'w'},
    {"tierfile", 1, NULL, 'x'},
    {"allocfile", 1, NULL, 'y'},
    {"relocfile", 0, NULL, 'z'},
    {"mntpt", 1, NULL, 'm'},
    {"sam", 0, NULL, 't'},
    {"files", 0, NULL, 'f'},
    {"options_rules", 1, NULL, 'o'},
    {0, 0, 0, 0}
  };

  char reloc_file[MAX_ARG_LENGTH] = "";
  char alloc_file[MAX_ARG_LENGTH] = "";
  char mountpoint[MAX_ARG_LENGTH] = "";
  char tier_file[MAX_ARG_LENGTH] = "";
  char device[MAX_ARG_LENGTH] = "";

  while ((opt = getopt_long(argc, argv,
			     "w:x:y:z:m:o:s r v f g a p i t e d n h",
			     longopts, NULL)) != -1) {

	switch (opt) {
	case 'h':
	  optionbits |= OPT_FLAG_H;
	  break;
	  /*
	   * Used to enable OHSM.
	   * ohsm -e ....
	   */

	case 'e':
	  optionbits |= OPT_FLAG_E;
	  break;

	  /*
	   * Used to disable OHSM.
	   * ohsm -s
	   */
	case 'd':
	  optionbits |= OPT_FLAG_D;
	  break;

	  /*
	   * Used to get the current status of OHSM.
	   * ohsm -n
	   */
	case 'n':
	  optionbits |= OPT_FLAG_N;
	  break;
	  /*
	   * used for debug ioctls.
	   * ohsm -v 1
	   * ohsm -v 2
	   * ohsm -v 3 <ino> <dest_tier>
	   */
	case 'v':
	  optionbits |= OPT_FLAG_V;
	  break;

	  /*
	   * Used to trigger relocation.
	   * ohsm -r <rule_no>
	   */
	case 'r':
	  optionbits |= OPT_FLAG_R;
	  break;

	case 'o':
	  rule = strtoul(optarg, NULL, 10);
	  optionbits |= OPT_FLAG_O;
	  break;

	  /*
	   * Used to pass the name of tier device file.
	   * ohsm <option> -i device.xml ....
	   */
	case 'i':
	  optionbits |= OPT_FLAG_I;
	  break;

	  /*
	   * Used to pass the name of allocation policy file.
	   * ohsm <option> -a alloc.xml ....
	   */

	case 'a':
	  optionbits |= OPT_FLAG_A;
	  break;

	  /*
	   * Used to pass the name of relocation policy file.
	   * ohsm <option> -o reloc.xml ....
	   */
	case 'p':
	  optionbits |= OPT_FLAG_P;
	  break;

	case 'f':
	  optionbits |= OPT_FLAG_F;
	  break;

	case 's':
	  optionbits |= OPT_FLAG_S;
	  break;

	case 'g':
	  optionbits |= OPT_FLAG_G;
	  break;

	case 'w':
	  strcpy(device, optarg);
	  optionbits |= OPT_FLAG_W;
	  break;

	case 'x':
	  strcpy(tier_file, optarg);
	  optionbits |= OPT_FLAG_X;
	  break;

	case 'y':
      strcpy(alloc_file, optarg);
	  optionbits |= OPT_FLAG_Y;
	  break;

	case 'z':
	  strcpy(reloc_file, optarg);
	  optionbits |= OPT_FLAG_Z;
	  break;

	case 'm':
	  strcpy(mountpoint, optarg);
	  optionbits |= OPT_FLAG_M;
	  break;

	case 't':
	  optionbits |= OPT_FLAG_T;
	  break;
	  /*
	   * Unused.
	   */

	case ':':
	  fprintf(stderr, "\nArgument Missing");
	  break;
	  /*
	   * Invalid option.
	   */
	case '?':
	  fprintf(stderr, "\nNo such options declared");
	  break;

    }
  }

  if (optionbits == OHSM_HELP) {
	ohsm_help();
	return 0;
  }

  ohsm_fd = open("/dev/ohsm", O_RDONLY);

  if (ohsm_fd <= 0) {
	fprintf(stderr, "\nOHSM: Problem with /dev/ohsm");
	fprintf(stderr, "\nOHSM: use cat /proc/devices");
	fprintf(stderr, "\nOHSM: mknod /dev/ohsm c major 0\n");
	return OHSM_ERROR;
  }

    switch (optionbits) {

    case OHSM_ENABLE:
      ret = ohsm_enable(alloc_file, reloc_file,
			 tier_file, mountpoint, device);
      break;

    case OHSM_DISABLE:
      ohsm_disable(mountpoint);
      break;

    case OHSM_RELOCATE:
      ohsm_relocate(mountpoint, 0);
      break;

    case OHSM_SELECT_RELOCATE:
      ohsm_relocate(mountpoint, rule);
      break;

    case OHSM_STATE:
      ohsm_status(mountpoint);
      break;

    case GET_ALLOC_POLICY:
      get_alloc_policy(mountpoint);
      break;

    case GET_RELOC_POLICY:
      get_reloc_policy(mountpoint);
      break;

    case GET_TIER_INFO:
      get_tier_info(mountpoint);
      break;

    case SET_ALLOC_POLICY:
      set_alloc_policy(mountpoint, alloc_file);
      break;

    case SET_RELOC_POLICY:
      set_reloc_policy(mountpoint, reloc_file);
      break;

    case SET_DEVICE_INFO:
      set_tier_info(mountpoint, device, tier_file);
      break;

    case DEBUG_RELOCATE:
      debug_relocate();
      break;

    case DEBUG_GET_SAM:
      debug_get_sam();
      break;

    case DEBUG_GET_FILES:
      debug_get_files();
      break;

    default:
      fprintf(stderr, "\nOHSM: Invalid option Specified %u", optionbits);
    }
  close(ohsm_fd);
  fprintf(stderr, "\n");
  return ret;
}
